package com.xunsiya.service.module.persons.impl;

import com.xunsiya.common.config.RequestUtils;
import com.xunsiya.common.config.SftpConfig;
import com.xunsiya.common.config.util.ConstantInfo;
import com.xunsiya.common.config.util.ImgUtil;
import com.xunsiya.module.base.DeleteFlag;
import com.xunsiya.module.device.dao.DeviceMapper;
import com.xunsiya.module.device.vo.Device;
import com.xunsiya.module.persons.dao.PersonsImageMapper;
import com.xunsiya.module.persons.dao.PersonsMapper;
import com.xunsiya.module.persons.vo.Persons;
import com.xunsiya.module.persons.vo.PersonsImage;
import com.xunsiya.module.persons.vo.PersonsVo;
import com.xunsiya.modules.algorithm.AbstractRecognize;
import com.xunsiya.modules.algorithm.AbstractResultParser;
import com.xunsiya.modules.algorithm.OpType;
import com.xunsiya.modules.algorithm.ResultVo;
import com.xunsiya.modules.algorithm.face.FaceResultParse;
import com.xunsiya.modules.algorithm.face.FaceSingleRecog;
import com.xunsiya.modules.algorithm.face.FaceUpdateDb;
import com.xunsiya.modules.algorithm.face.vo.FaceResultVo;
import com.xunsiya.modules.algorithm.face.vo.MatchDetect;
import com.xunsiya.modules.algorithm.face.vo.MatchFace;
import com.xunsiya.modules.algorithm.rpc.RPCReturnCode;
import com.xunsiya.service.module.persons.PersonsService;
import com.xunsiya.system.dao.DictMapper;
import com.xunsiya.system.vo.Dict;
import com.xunsiya.system.vo.User;
import com.xunsiya.tools.common.config.PathUtils;
import com.xunsiya.tools.common.msg.Msg;
import com.xunsiya.tools.common.pagination.ForePage;
import com.xunsiya.tools.common.pagination.PagedUtil;
import com.xunsiya.tools.common.random.RandomUtil;
import com.xunsiya.tools.common.string.StringUtil;
import com.xunsiya.tools.file.cryptography.FileMD5Util;
import com.xunsiya.tools.file.type.FileTypeJudge;
import com.xunsiya.tools.file.type.MediaFileType;
import org.apache.commons.io.FileUtils;
import org.apache.log4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import java.io.File;
import java.io.IOException;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Map;

/**
 * 人物管理Service实现
 *
 * @author panjun
 * @version 1.0
 *          <p>
 *          <p>修订人		修订时间			描述信息
 *          <p>-----------------------------------------------------
 *          <p>panjun		2018/11/2		初始创建
 */
@Service("personsService")
@Transactional(rollbackFor = Exception.class)
public class PersonsServiceImpl implements PersonsService {
    /**
     * 日志
     */
    private static Logger log = Logger.getLogger(PersonsServiceImpl.class);

    @Autowired
    private PersonsMapper personsMapper;

    @Autowired
    private PersonsImageMapper personsImageMapper;

    @Autowired
    private DeviceMapper deviceMapper;

    @Autowired
    private DictMapper dictMapper;

    @Value("${face.linux-root-path}")
    private String facePath;

    @Value("${face.linux-show-path}")
    private String faceShow;

    @Value("${algorithm.face-xmlrpc-name}")
    private String faceModuleName;

    @Autowired
    private SftpConfig sftpConfig;

    @Value("${face.operation-type}")
    private String operationType;

    @Value("${person.image-width}")
    private Integer imageWidth;

    @Value("${person.image-height}")
    private Integer imageHeight;

    @Value("${person.head-width}")
    private Integer headWidth;

    @Value("${person.head-height}")
    private Integer headHeight;

    private final static String PERSON_TYPE_NAME = "person_type";

    /**
     * 保存
     *
     * @param persons 人物信息
     * @param path    人物头像
     * @return
     */
    @Override
    public Msg save(Persons persons, String path) {
        Msg msg = new Msg(false);
        // 当前用户
        User user = RequestUtils.getSessionAttribte(User.SYS_LOGIN_INFO);

        boolean bool = true;
        // id 为空添加，否则更新
        if (StringUtil.isEmpty(persons.getId())) {

            // 查询人物名称是否存在
            Persons person = personsMapper.queryByName(persons);
            if (person != null) {
                msg.setMsg("该人物已存在");
                return msg;
            }

            // 添加
            persons.setStatus(DeleteFlag.NORMAL.getValue());
            persons.setId(RandomUtil.getGUID());
            persons.setCreateTime(new Date());
            persons.setUpdateTime(new Date());
            persons.setAuthor(user.getId());
            persons.setExtraInfo(persons.getCompanyId() + "");

            // 头像入算法
            if (!StringUtil.isEmpty(path)) {
                msg = uploadHeadImgAlg(persons, path);
                if (!msg.isSuccess()) {
                    return msg;
                }
            }

            bool = personsMapper.insert(persons);
            //return new Msg(bool, bool ? "保存成功" : "保存失败");
        } else {
            persons.setUpdateTime(new Date());
            // 头像入算法
            if (!StringUtil.isEmpty(path)) {
                msg = uploadHeadImgAlg(persons, path);
                if (!msg.isSuccess()) {
                    return msg;
                }
            }

            // 更新
            bool = personsMapper.update(persons);
            //return new Msg(bool, bool ? "保存成功" : "保存失败");
        }

        if (!StringUtil.isEmpty(path) && bool) {
            // 头像保存到图册里
            PersonsImage personsImage = new PersonsImage();
            personsImage.setPoliticallyPersonsId(persons.getId());
            personsImage.setPath(persons.getHeadImage());
            personsImage.setMd5(persons.getMd5());

            // 查询是否存在相同的图片
            List<PersonsImage> list = personsImageMapper.findList(personsImage);
            if (!CollectionUtils.isEmpty(list)) {
                return new Msg(true, "图片已存在");
            }

            // 保存图像
            personsImage.setStatus(ConstantInfo.DelType.NORMAL.getType());
            personsImage.setCreateTime(new Date());
            personsImage.setUpdateTime(new Date());
            personsImage.setAuthor(user.getId());
            bool = personsImageMapper.insert(personsImage);
        }
        return new Msg(bool, bool ? "保存成功" : "保存失败");
    }

    /**
     * 分页查询
     *
     * @param persons   人物信息
     * @param pageIndex
     * @param pageSize  每页数量
     * @return
     */
    @Override
    public ForePage<Persons> findPage(Persons persons, Integer pageIndex, Integer pageSize) {
        if (StringUtil.isEmpty(persons.getName())) {
            persons.setName("%");
        } else {
            persons.setName("%" + persons.getName() + "%");
        }

        int count = personsMapper.queryCount(persons);
        List<Persons> list = personsMapper.queryForPage(persons, PagedUtil.getStartIndex(pageIndex, pageSize), pageSize);
        // 循环替换图片路径
        for (Persons e : list) {
            if (!StringUtil.isEmpty(e.getHeadImage())) {
                // 缩放图片长宽
                String style = "border-radius: 10px; " + ImgUtil.zoomImage(e.getHeadImage(), headWidth, headHeight);
                e.setStyle(style);
                e.setHeadImage(e.getHeadImage().replace(PathUtils.getBasePath(), PathUtils.getBaseUrl()));
            }
        }
        return new ForePage<>(count, pageIndex, list);
    }

    /**
     * 删除
     *
     * @param persons 人物
     * @return
     */
    @Override
    public int delete(Persons persons) {
        persons.setStatus(DeleteFlag.DELETE.getValue());
        boolean bool = personsMapper.delete(persons);
        return bool ? 1 : 0;
    }

    /**
     * 查询人物图片
     *
     * @param personsImage 人物图片
     * @return
     */
    @Override
    public List<PersonsImage> findList(PersonsImage personsImage) {
        // 查询人物图片
        List<PersonsImage> list = personsImageMapper.findList(personsImage);
        // 循环替换图片路径
        for (PersonsImage e : list) {
            if (!StringUtil.isEmpty(e.getPath())) {
                // 缩放图片长宽
                String style = "position: absolute;border-radius: 10px;top: 50%;left: 50%; transform: translate(-50%,-50%); " + ImgUtil.zoomImage(e.getPath(), imageWidth, imageHeight);
                e.setStyle(style);
                e.setPath(e.getPath().replace(PathUtils.getBasePath(), PathUtils.getBaseUrl()));
            }
        }
        return list;
    }

    /**
     * 保存人物图片
     *
     * @param personsImage 人物信息
     * @return
     */
    @Override
    public Msg insert(PersonsImage personsImage) {
        File faceFile = new File(personsImage.getPath());
        if (!faceFile.exists()) {
            log.warn("待加载的文件不存在或者是一个目录");
            return new Msg(false, "待加载的文件不存在或者是一个目录");
        }

        try {
            if (!FileTypeJudge.isImage(faceFile.getAbsolutePath())) {
                log.warn("待加载的文件[" + faceFile.getAbsolutePath() + "]不是图片");
                return new Msg(false, "待加载的文件不是图片");
            }
        } catch (IOException e) {
            log.error("错误", e);
        }

        // 当前用户
        User user = RequestUtils.getSessionAttribte(User.SYS_LOGIN_INFO);
        // 文件名称
        String mediaName = faceFile.getName();

        String md5 = "";
        try {
            md5 = FileMD5Util.getMD5FromFile(faceFile);
        } catch (Exception e) {
            log.error("错误", e);
        }
        log.info("MD5: " + md5);
        personsImage.setMd5(md5);

        String companyId = personsImage.getCompanyId() + "";

        // 媒体文件存储目录
        String destFaceDir = facePath + companyId + "/" + personsImage.getPoliticallyPersonsId();
        if (!new File(destFaceDir).exists()) {
            new File(destFaceDir).mkdirs();
        }

        // 文件后缀
        String type = mediaName.indexOf(".") != -1 ? mediaName.substring(mediaName.lastIndexOf(".") + 1, mediaName.length()) : null;
        // 文件名称
        String name = type != null ? md5 + "." + type : md5 + ".jpg";


        String destFilePath = destFaceDir + "/" + mediaName;
        try {
            //将文件移到指定路径下,并且重命名
            FileUtils.moveFile(faceFile, new File(destFilePath));
        } catch (IOException e) {
            log.error("错误", e);
        }

        // 查询是否存在相同的图片
        List<PersonsImage> list = personsImageMapper.findList(personsImage);
        if (!CollectionUtils.isEmpty(list)) {
            return new Msg(false, "图片已存在");
        }

        // 保存人脸指纹库
        Msg msg = saveAlg(companyId, personsImage.getPoliticallyPersonsId(), destFilePath, md5);
        if (!msg.isSuccess()) {
            return msg;
        }

        // 添加
        personsImage.setPath(destFilePath);
        personsImage.setStatus(ConstantInfo.DelType.NORMAL.getType());
        personsImage.setCreateTime(new Date());
        personsImage.setUpdateTime(new Date());
        personsImage.setAuthor(user.getId());
        Boolean bool = personsImageMapper.insert(personsImage);

        return new Msg(bool, bool ? "添加成功" : "添加失败");
    }

    /**
     * 保存人脸指纹库
     *
     * @param companyId 公司编号
     * @param personId 人物编号
     * @param destFilePath 图片地址
     * @param md5 图片md5
     * @return
     */
    private Msg saveAlg(String companyId, String personId, String destFilePath, String md5) {
        // 初始化更新人脸指纹库
        FaceUpdateDb faceUpdateDb = new FaceUpdateDb(faceModuleName, OpType.ADD, companyId, personId, destFilePath, md5);
        Map<String, Object> map = null;
        try {
            // 添加
            map = faceUpdateDb.updateSingleDB();
        } catch (Exception e) {
            log.error("错误", e);
        }

        if (CollectionUtils.isEmpty(map)) {
            log.info("报错:XMLRPC无返回");
            return new Msg(false, "报错:XMLRPC无返回");
        }


        String faceId = map.get("result").toString();
        int result = Integer.parseInt(faceId);
        if (result >= RPCReturnCode.SUCCESS.getValue()) {
            log.info("特征库入库成功!");
        } else {
            log.info("返回报错: " + result);
            if (result != RPCReturnCode.OBJ_EXIST.getValue()) {
                //已经添加此指纹
                return new Msg(false, map.get("url") + "");
            } else {
                return new Msg(false, "指纹已存在");
            }
        }
        return new Msg(true);
    }

    /**
     * 删除人物图片
     *
     * @param personsImage 人物图片
     * @return
     */
    @Override
    public Msg delete(PersonsImage personsImage) {
        // 当前用户
        User user = RequestUtils.getSessionAttribte(User.SYS_LOGIN_INFO);
        String companyId = personsImage.getCompanyId() + "";
        // 查询要删除的图片
        personsImage = personsImageMapper.queryById(personsImage.getId());
        if (personsImage == null) {
            return new Msg(false, "删除失败");
        }

        // 初始化更新人脸指纹库
        FaceUpdateDb faceUpdateDb = new FaceUpdateDb(faceModuleName, OpType.DELETE, companyId + "", personsImage.getPoliticallyPersonsId(), personsImage.getMd5());
        Map<String, Object> map = null;
        try {
            // 添加
            map = faceUpdateDb.updateSingleDB();
        } catch (Exception e) {
            log.error("错误", e);
        }

        if (CollectionUtils.isEmpty(map)) {
            log.info("报错:XMLRPC无返回");
            return new Msg(false, "报错:XMLRPC无返回");
        }

        int result = Integer.parseInt(map.get("result").toString());
        if (result == RPCReturnCode.SUCCESS.getValue()) {
            log.info("算法库删除成功!");
            personsImage.setStatus(ConstantInfo.DelType.DELETE.getType());
            boolean bool = personsImageMapper.delete(personsImage);
            return new Msg(bool, bool ? "删除成功" : "删除失败");
        } else {
            log.info("算法库删除失败，返回报错: " + result);
            return new Msg(false, "删除失败");
        }
    }

    /**
     * 识别人脸
     * @param deviceId 设备编号
     * @param path 文件路径
     * @return
     */
    @Override
    public List<PersonsVo> recogFace(String deviceId, String path) {
        // 根据蓝牙地址查询设备信息
        Device device = deviceMapper.findByGuid(deviceId);
        if (device == null) {
            log.info("设备信息不存在");
            return null;
        }

        // 人脸识别
        return recogFile(path, device.getCompanyId() + "");
    }


    /**
     * 人脸识别
     *
     * @param filePath 图片地址
     * @param companyId 公司编号
     * @return
     */
    private List<PersonsVo> recogFile(String filePath, String companyId) {
        try {
            // 文件
            File file = new File(filePath);

            // MD5
            String fileMd5 = FileMD5Util.getMD5FromFile(file);
            log.info("人脸识别文件：" + filePath);
            MediaFileType fileType;
            if (FileTypeJudge.isImage(filePath)) {
                fileType = MediaFileType.IMAGE;
            } else if (FileTypeJudge.isVideo(filePath)) {
                fileType = MediaFileType.VIDEO;
            } else {
                log.warn("待加载的文件[" + filePath + "]不是图片也不是视频");
                return null;
            }
            log.info("人脸识别文件类型：" + fileType);
            log.info("人脸识别开始");
            String resultFile = facePath + companyId + File.separator + fileMd5 + ConstantInfo.ANALYSIS_TXT_SUFFIX;
            // 人脸识别初始化
            AbstractRecognize faceSingleRecog = new FaceSingleRecog(fileType, faceModuleName, operationType, filePath, fileMd5, companyId, resultFile);
            // 人脸识别
            int code = faceSingleRecog.processFile();
            log.info("人脸识别结果：" + code);
            if (code < RPCReturnCode.SUCCESS.getValue()) {
                return null;
            }
            log.info("人脸识别结束");

            // ocr结果解析初始化
            AbstractResultParser faceResultParser = new FaceResultParse();
            // ocr结果解析
            ResultVo<List<FaceResultVo>> resultVo = faceResultParser.parse(resultFile, 300);

            // 返回结果
            // 搜索返回的图片。视频
            List<PersonsVo> personsList = new ArrayList<>();
            if (MediaFileType.IMAGE.getValue() == fileType.getValue()) {
                // 图片人物识别结果解析
                resultVo.getData().forEach(vo -> personsList.addAll(analysisImage(vo)));
            } else {
                // 视频人物识别结果解析
                resultVo.getData().forEach(vo -> personsList.addAll(analysisVideo(vo)));
            }

            return personsList;
        } catch (Exception e) {
            log.error("出错的内容：" + e.toString());
        }
        return null;
    }

    /**
     * 图片人物识别结果解析
     *
     * @param data 识别结果
     * @return
     */
    private List<PersonsVo> analysisImage(FaceResultVo data) {
        MatchFace matchFace = null;
        List<PersonsVo> personsVoList = new ArrayList<>();
        PersonsVo persons;
        for (MatchDetect matchDetect : data.getFaceResults()) {
            // 循环匹配的人物
            for (MatchFace e : matchDetect.getDetects()) {
                boolean bool = null == matchFace || (matchFace != null && !matchFace.getClassName().equals(e.getClassName()));
                if (bool) {
                    matchFace = e;
                    persons = new PersonsVo();

                    // 人物
                    String className = e.getClassName();
                    log.info("人脸识别的人物：" + className);
                    log.info("人脸识别的分数：" + e.getClassScore());

                    // 取出小于0的结果
                    if (e.getClassScore() <= 0) {
                        continue;
                    }

                    // 查询人物信息
                    queryPerson(className, persons, matchFace.getClassScore());
                    if (persons == null || StringUtil.isEmpty(persons.getName())) {
                        continue;
                    }

                    personsVoList.add(persons);
                }
            }

        }

        return personsVoList;
    }

    /**
     * 视频人物识别结果解析
     *
     * @param data 识别结果
     * @return
     */
    private List<PersonsVo> analysisVideo(FaceResultVo data) {

        List<PersonsVo> personsVoList = new ArrayList<>();
        PersonsVo persons;
        for (MatchDetect matchDetect : data.getFaceResults()) {
            // 循环匹配的人物
            for (MatchFace matchFace : matchDetect.getDetects()) {
                persons = new PersonsVo();
                // 人物
                String className = matchFace.getClassName();
                String classPath = matchFace.getImagePath();
                log.info("人脸识别的人物：" + className);
                log.info("人脸识别的分数：" + matchFace.getClassScore());
                log.info("人脸识别的截图：" + classPath);

                // 查询人物信息
                queryPerson(className, persons, matchFace.getClassScore());
                if (persons == null) {
                    continue;
                }

                personsVoList.add(persons);
            }

        }
        return personsVoList;
    }

    /**
     * 查询人物信息
     *
     * @param className 人物编号
     * @param persons 人物返回对象
     * @param score 分数
     */
    private void queryPerson(String className, PersonsVo persons, Float score) {
        // 查询人物名称
        Persons person = personsMapper.queryById(className);
        if (person == null) {
            return;
        }

        persons.setFaceUrl(person.getHeadImage().replace(PathUtils.getBasePath(), sftpConfig.getBaseUrl()));
        persons.setName(person.getName());
        // 计算
        BigDecimal bd = new BigDecimal(score * 100);
        bd = bd.setScale(0, BigDecimal.ROUND_HALF_UP);
        // 分数
        persons.setScore(bd.doubleValue());

        // 查询人物类型
        Dict dict = dictMapper.findByValue(PERSON_TYPE_NAME, person.getType());
        persons.setType(dict == null ? person.getType() : dict.getLabel());
    }

    /**
     * 头像入算法
     *
     * @param persons 人物信息
     * @param path    图片地址
     */
    private Msg uploadHeadImgAlg(Persons persons, String path) {
        try {
            // 图片
            File file = new File(path);
            // MD5
            String md5 = FileMD5Util.getMD5FromFile(file);
            // 文件后缀
            String type = path.indexOf(".") != -1 ? path.substring(path.lastIndexOf(".") + 1, path.length()) : null;
            // 文件名称
            String name = type != null ? md5 + "." + type : md5 + ".jpg";

            String newPath = faceShow + persons.getCompanyId() + "/" + persons.getId();
            // 复制文件
            if (!new File(newPath).exists()) {
                new File(newPath).mkdirs();
            }

            FileUtils.copyFile(file, new File(newPath  + "/" + name));
            // 头像入算法库
            // 保存人脸指纹库
            Msg msg = saveAlg(persons.getCompanyId() + "", persons.getId(), newPath  + "/" + name, md5);
            if (!msg.isSuccess()) {
                return msg;
            }

            persons.setHeadImage(newPath  + "/" + name);
            persons.setMd5(md5);

        } catch (Exception e) {
            log.error("头像入算法错误");
        }
        return new Msg(true);
    }
}
